package core;

import java.util.EnumMap;
import java.util.Map;

/**
 * An enum representing a UnitOfMeasurement, as used by Amount
 *
 */
public enum UnitOfMeasurement
{
        
        
        count {
                public String toString() {
                        return "count";
                }
        },
        pounds {
                public String toString() {
                        return "pounds";
                }
        },
        ounces {
                public String toString() { 
                        return "ounces";
                }
        },
        grams {
                public String toString() { 
                        return "grams";
                }
        },
        kilograms {
                public String toString() {
                        return "kilograms";
                }
        },
        gallons {
                public String toString() {
                        return "gallons";
                }
        },
        quarts {
                public String toString() { 
                        return "quarts";
                }
        },
        pints {
                public String toString() {
                        return "pints"; 
                }
        },
        fluid_ounces {
                public String toString() {
                        return "fluid ounces"; 
                }
        },
        liters {
                public String toString() {
                        return "liters";
                }
        };

	private static float LBS_TO_OZ = 16F;
	private static float OZ_TO_GRAMS = 28.3495F;
	private static float KG_TO_GRAMS = 1000F;
	private static float PINT_TO_FLOZ = 16F;
	private static float QT_TO_PINT = 2F;
	private static float GAL_TO_QT = 4F;
	private static float LITER_TO_QT = 1.05669F;
	
	private static Map<UnitOfMeasurement, Map<UnitOfMeasurement, Float>> conversionMap;
	
	//static block for generating conversion maps
	static {
		
		conversionMap = new EnumMap<UnitOfMeasurement, 
				Map<UnitOfMeasurement, Float>>(UnitOfMeasurement.class);
		
		Map<UnitOfMeasurement, Float> countMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		conversionMap.put(count, countMap);
		
		Map<UnitOfMeasurement, Float> poundsMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		poundsMap.put(ounces, (1/LBS_TO_OZ));
		poundsMap.put(grams, (1/OZ_TO_GRAMS) * (1/LBS_TO_OZ));
		poundsMap.put(kilograms, (KG_TO_GRAMS * (1/OZ_TO_GRAMS) * (1/LBS_TO_OZ)));
		conversionMap.put(pounds, poundsMap);
		
		Map<UnitOfMeasurement, Float> ouncesMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		ouncesMap.put(pounds, LBS_TO_OZ);
		ouncesMap.put(grams, (1/OZ_TO_GRAMS));
		ouncesMap.put(kilograms, (KG_TO_GRAMS * (1/OZ_TO_GRAMS)));
		conversionMap.put(ounces, ouncesMap);
		
		Map<UnitOfMeasurement, Float> gramsMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		gramsMap.put(pounds, (LBS_TO_OZ * OZ_TO_GRAMS));
		gramsMap.put(ounces, OZ_TO_GRAMS);
		gramsMap.put(kilograms, KG_TO_GRAMS);
		conversionMap.put(grams, gramsMap);
		
		Map<UnitOfMeasurement, Float> kiloMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		kiloMap.put(pounds, (LBS_TO_OZ * OZ_TO_GRAMS * (1/KG_TO_GRAMS)));
		kiloMap.put(ounces, (OZ_TO_GRAMS * (1/KG_TO_GRAMS)));
		kiloMap.put(grams, (1 / KG_TO_GRAMS));
		conversionMap.put(kilograms, kiloMap);
		
		Map<UnitOfMeasurement, Float> gallonsMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		gallonsMap.put(quarts, (1/GAL_TO_QT));
		gallonsMap.put(pints, ((1/QT_TO_PINT) * (1/GAL_TO_QT)));
		gallonsMap.put(fluid_ounces, ((1/PINT_TO_FLOZ) * (1/QT_TO_PINT) * (1/GAL_TO_QT)));
		gallonsMap.put(liters, (LITER_TO_QT * (1/GAL_TO_QT)));
		conversionMap.put(gallons, gallonsMap);
		
		Map<UnitOfMeasurement, Float> quartsMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		quartsMap.put(gallons, GAL_TO_QT);
		quartsMap.put(pints, (1/QT_TO_PINT));
		quartsMap.put(fluid_ounces, ((1/PINT_TO_FLOZ) * (1/QT_TO_PINT)));
		quartsMap.put(liters, LITER_TO_QT);
		conversionMap.put(quarts, quartsMap);
		
		Map<UnitOfMeasurement, Float> pintsMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		pintsMap.put(gallons, (GAL_TO_QT * QT_TO_PINT));
		pintsMap.put(quarts, QT_TO_PINT);
		pintsMap.put(fluid_ounces, (1/PINT_TO_FLOZ));
		pintsMap.put(liters, (LITER_TO_QT * QT_TO_PINT));
		conversionMap.put(pints, pintsMap);
		
		Map<UnitOfMeasurement, Float> flozMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		flozMap.put(gallons, (GAL_TO_QT * QT_TO_PINT * PINT_TO_FLOZ));
		flozMap.put(quarts, (QT_TO_PINT * PINT_TO_FLOZ));
		flozMap.put(pints, PINT_TO_FLOZ);
		flozMap.put(liters, (LITER_TO_QT * QT_TO_PINT * PINT_TO_FLOZ));
		conversionMap.put(fluid_ounces, flozMap);
		
		Map<UnitOfMeasurement, Float> litersMap = 
				new EnumMap<UnitOfMeasurement, Float>(UnitOfMeasurement.class);
		litersMap.put(gallons, (GAL_TO_QT * (1/LITER_TO_QT)));
		litersMap.put(quarts,(1/LITER_TO_QT));
		litersMap.put(pints, ((1/QT_TO_PINT) * (1/LITER_TO_QT)));
		litersMap.put(fluid_ounces, ((1/PINT_TO_FLOZ) * (1/QT_TO_PINT) * (1/LITER_TO_QT)));
		conversionMap.put(liters, litersMap);
	}
	
	/**
	 * Gets a conversion factor from b to this
	 * @param b the unit to convert to this unit
	 * @return the conversion factor, or null if it does not exist
	 */
	protected Float convertFactorTo(UnitOfMeasurement b) {
		if (b == this) {
			return 1F;
		}
		
		Map<UnitOfMeasurement, Float> myMap = conversionMap.get(this);
		return myMap.get(b);
		
	}
	

}